﻿using System;
using System.Collections;
using System.Threading.Tasks;
using UnityEngine;

namespace winS.Unity
{
    /// <summary>
    /// 协程的核心运行类
    /// </summary>
    internal class CoroutineCore
    {
        public event Action onCompleted;

        /// <summary>
        /// 协程名称
        /// </summary>
        public string name;

        private bool timeScale;
        private bool requestStop;

        private float duration;
        private float currentTime;

        private WaitType waitType;
        private IEnumerator enumerator;
        private CoroutineState coroutineState;
        private ICustomCoroutineWait customCoroutineWait;

        private Task task;
        private Func<bool> nextFunction;
        private Coroutine child;
        private AsyncOperation asyncOperation;
        private CoroutineCore enumeratorCoroutine;

        public CoroutineCore(IEnumerator enumerator)
        {
            Initialize(enumerator);
        }

        public void Release()
        {
            name = null;
            onCompleted = null;
            timeScale = requestStop = false;
            child = null;
            duration = currentTime = 0f;

            waitType = WaitType.None;
            enumerator = null;
            customCoroutineWait = null;
            enumeratorCoroutine = null;

            task = null;
            nextFunction = null;
            asyncOperation = null;
        }
        public void RequestStop()
        {
            requestStop = true;
        }
        public void Initialize(IEnumerator enumerator)
        {
            this.enumerator = enumerator;
            coroutineState = CoroutineState.Running;
        }

        /// <summary>
        /// 更新协程
        /// </summary>
        internal CoroutineState Update()
        {
            RecursiveUpdate();

            //要注意的是, 运行协程的过程中, 协程可能被Stop
            //此时有2种情况:
            //1.这个协程在Stop自己后, 依旧没有完成
            //2.这个协程在Stop自己后, 成功的完成
            //无论哪种情况, 我们都认为 Stop 大于 Completed, 我们不发出 onCompleted 事件
            if (requestStop)
            {
                CoroutineStarter.Remove(this);
                //停止子协程(如果它受到父级影响)
                if (child != null && !child.unaffectedByParentStop) child.Stop();
                return coroutineState = CoroutineState.Stopped;
            }

            //没有把自己Stop的情况并且成功完成
            if (coroutineState == CoroutineState.Completed)
            {
                CoroutineStarter.Remove(this);
                onCompleted?.Invoke();
            }

            return coroutineState;
        }

        private void RecursiveUpdate()
        {
            //由于是递归调用, 这里有2种情况:
            //协程在运行前就已经被外部要求停止
            //协程在自我递归调用的某个阶段 (XXXXWait()->Continue()->RecursiveUpdate()->XXXXWait()->Continue()->....) 把自己停止
            //不管哪种情况, 我们都停止更新
            if (requestStop)
            {
                coroutineState = CoroutineState.Stopped;
                return;
            }
            //运行协程
            switch (waitType)
            {
                case WaitType.None: NoneWait(); break;
                case WaitType.Time: TimeWait(); break;
                case WaitType.Task: TaskWait(); break;
                case WaitType.Custom: CustomWait(); break;
                case WaitType.Function: FunctionWait(); break;
                case WaitType.Coroutine: CoroutineWait(); break;
                case WaitType.Enumerator: EnumeratorWait(); break;
                case WaitType.AsyncOperation: AsyncOperationWait(); break;
            }
        }

        /// <summary>
        /// 没有任何等待, 进行下一步
        /// </summary>
        private void NoneWait()
        {
            if (!enumerator.MoveNext())//枚举器结束, 协程正常完成
            {
                coroutineState = CoroutineState.Completed;
                return;
            }
            //在枚举器运行期间, 自身被Stop
            if (requestStop)
            {
                coroutineState = CoroutineState.Stopped;
                return;
            }
            object currentObject = enumerator.Current;
            if (currentObject == null) return;
            if (currentObject is Wait wait)
            {
                if (wait.duration < Time.deltaTime) Continue();//如果等待的时间小于当前帧间隔, 不要等待下一帧, 直接继续向下, 其它类型同理
                else
                {
                    currentTime = 0f;
                    timeScale = true;
                    duration = wait.duration;
                    waitType = WaitType.Time;
                }
                return;
            }
            if (currentObject is IEnumerator childEnumerator)//拥有一个枚举器
            {
                CoroutineCore coroutineCore = CoroutineCorePool.Pop(childEnumerator);
                if (coroutineCore.Update() != CoroutineState.Running) Continue();
                else
                {
                    enumeratorCoroutine = coroutineCore;
                    waitType = WaitType.Enumerator;
                }
                return;
            }
            if (currentObject is Coroutine coroutine)//拥有一个子协程
            {
                //有以下2种有问题的情况:
                //1.这个子协程在一帧内就完成或早已完成, 但用户再次去等待
                if (!coroutine.running)
                {
                    Continue();
                    return;
                }
                //2.这个子协程与当前协程有父子关系, 可能死循环, 检查父子关系
                CoroutineCore current = coroutine.coroutineCore;
                do
                {
                    if (current == this)
                    {
                        Continue();
                        return;
                    }
                    current = current.child?.coroutineCore;
                } while (current != null);
                //否则, 我们认为用户开启了一个全新的子协程, 等它执行完毕
                child = coroutine;
                waitType = WaitType.Coroutine;
                return;
            }
            if (currentObject is Task task)//拥有一个Task
            {
                if (task.IsCompleted) Continue();
                else
                {
                    this.task = task;
                    waitType = WaitType.Task;
                }
                return;
            }
            if (currentObject is WaitUntilTrue waitUntilTrue)
            {
                if (waitUntilTrue.function()) Continue();
                else
                {
                    waitType = WaitType.Function;
                    nextFunction = waitUntilTrue.function;
                }
                return;
            }
            if (currentObject is WaitRealtime waitRealtime)
            {
                if (waitRealtime.duration < Time.unscaledDeltaTime) Continue();
                else
                {
                    currentTime = 0f;
                    waitType = WaitType.Time;
                    duration = waitRealtime.duration;
                }
                return;
            }
            if (currentObject is ICustomCoroutineWait customCoroutineWait)
            {
                if (customCoroutineWait.customCoroutineWaitComplete) Continue();
                else
                {
                    waitType = WaitType.Custom;
                    this.customCoroutineWait = customCoroutineWait;
                }
                return;
            }
            if (currentObject is AsyncOperation asyncOperation)
            {
                if (asyncOperation.isDone) Continue();
                else
                {
                    this.asyncOperation = asyncOperation;
                    waitType = WaitType.AsyncOperation;
                }
                return;
            }
        }

        private void Continue()
        {
            waitType = WaitType.None;
            RecursiveUpdate();
        }
        private void TaskWait()
        {
            if (!task.IsCompleted) return;
            Continue();
        }
        private void TimeWait()
        {
            currentTime += timeScale ? Time.deltaTime : Time.unscaledDeltaTime;
            if (currentTime < duration) return;
            Continue();
        }
        private void CustomWait()
        {
            if (!customCoroutineWait.customCoroutineWaitComplete) return;
            Continue();
        }
        private void FunctionWait()
        {
            if (!nextFunction()) return;
            Continue();
        }
        private void CoroutineWait()
        {
            if (child.running) return;
            Continue();
        }
        private void EnumeratorWait()
        {
            if (enumeratorCoroutine.Update() == CoroutineState.Running) return;
            Continue();
        }
        private void AsyncOperationWait()
        {
            if (!asyncOperation.isDone) return;
            Continue();
        }
    }
}